# -*- coding: utf-8 -*-

# Copyright (c) 2007 Juan Manuel Gimeno Illa <jmgimeno@gmail.com>

# License:
# 
#     This program is free software; you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation; either version 2 of the License, or
#     (at your option) any later version.
# 
#     This program is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
# 
#     You should have received a copy of the GNU General Public License
#     along with this program; if not, write to the Free Software Foundation,
#     Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
# 
# A full copy of the GPL can be found in /usr/share/common-licenses/GPL on most
# Debian systems.

import deskbar
import deskbar.Handler
import deskbar.Match
import gtk
import ldap

from gettext import gettext as _

GCONF_LDAP_HOST = deskbar.GCONF_DIR+"/ldap/path"
GCONF_LDAP_PORT = deskbar.GCONF_DIR+"/ldap/port"
GCONF_LDAP_BASE = deskbar.GCONF_DIR+"/ldap/base"
GCONF_LDAP_WHO  = deskbar.GCONF_DIR+"/ldap/who"
GCONF_LDAP_CRED = deskbar.GCONF_DIR+"/ldap/cred"
    
def _check_requirements():
    if not deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_HOST) or \
        not deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_PORT) or \
        not deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_BASE):
        return (deskbar.Handler.HANDLER_HAS_REQUIREMENTS, 
            _("You must configure the LDAP server before using it"),
            _on_config_server)
    else:
        return (deskbar.Handler.HANDLER_IS_CONFIGURABLE, 
            _("Some parameters of the LDAP server can be configured"),
            _on_config_server)

def _add_labeled_entry_at_row(table, row, name, text):
    label = gtk.Label(name)
    entry = gtk.Entry()
    entry.set_text(text)
    table.attach(label, 0, 1, row, row+1)
    table.attach(entry, 1, 2, row, row+1)
    return entry
    
def _on_config_server(parent):
    dialog = gtk.Dialog(
        _("LDAP Configuration"), 
        parent,
        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
        (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
    host, base, port, who, cred = _get_ldap_gconf()
    table = gtk.Table(rows=5, columns=2)
    host_entry = _add_labeled_entry_at_row(table, 0, _("Host"), host)
    port_entry = _add_labeled_entry_at_row(table, 1, _("Port"), port)
    base_entry = _add_labeled_entry_at_row(table, 2, _("Base"), base)
    who_entry  = _add_labeled_entry_at_row(table, 3, _("Who"),  who)
    cred_entry = _add_labeled_entry_at_row(table, 4, _("Cred"), cred)
    table.show_all()
    dialog.vbox.add(table)
    response = dialog.run()
    dialog.destroy()
    if response == gtk.RESPONSE_ACCEPT:
        _update_gconf_from_entry(GCONF_LDAP_HOST, host_entry)
        _update_gconf_from_entry(GCONF_LDAP_PORT, port_entry)
        _update_gconf_from_entry(GCONF_LDAP_BASE, base_entry)
        _update_gconf_from_entry(GCONF_LDAP_WHO,  who_entry)
        _update_gconf_from_entry(GCONF_LDAP_CRED, cred_entry)
        
def _update_gconf_from_entry(key, entry):
    if entry.get_text() != "":
        deskbar.GCONF_CLIENT.set_string(key, entry.get_text())

def _get_ldap_gconf():
    host = deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_HOST) or ""
    base = deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_BASE) or ""
    port = deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_PORT) or ""
    who  = deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_WHO)  or ""
    cred = deskbar.GCONF_CLIENT.get_string(GCONF_LDAP_CRED) or ""
    return host, base, port, who, cred
    
class LDAPHandler(deskbar.Handler.Handler):
    def __init__ (self):
        deskbar.Handler.Handler.__init__ (self, "nobody.png")
        host, base, port, who, cred = _get_ldap_gconf()
        self.ld = LDAPServer(host, port, who, cred, base)
    def query(self, text):
        return [LDAPMatch(self, text)]
    
class LDAPMatch(deskbar.Match.Match):
    "This class represents and LDAP entry"
    def __init__(self, backend, fragment, **kwargs):
        deskbar.Match.Match.__init__(self, backend, **kwargs)
        self.backend = backend
        self.fragment = fragment
    def get_verb(self):
        return _("Search in LDAP for <b>%s</b>") % self.fragment
    def get_category(self):
        return "ldap"
    def action(self, text=None):
        try:
            entries = self.backend.ld.find_by_name_fragment(text)
            if entries:
                dlg = make_results_dialog(entries, text)
                dlg.show_all()
                #win.run()
                #win.destroy()
            else:
                dlg = gtk.MessageDialog(
                    type=gtk.MESSAGE_INFO,
                    buttons=gtk.BUTTONS_CLOSE,
                    message_format=_("No matches in LDAP"))
                dlg.run()
                dlg.destroy()
        except ldap.LDAPError:
            dlg = gtk.MessageDialog(
                type=gtk.MESSAGE_ERROR, 
                buttons=gtk.BUTTONS_CLOSE, 
                message_format=_("Unable to connect to LDAP server"))
            dlg.run()
            dlg.destroy()

def make_results_dialog(entries, text):
    dialog = gtk.Dialog(
        title=_("LDAP entries matching %s") % text)
    dialog.set_position(gtk.WIN_POS_CENTER)
    dialog.set_size_request(600,400)
    close_btn = dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
    close_btn.connect("clicked", lambda widget, data: dialog.destroy(), None)
    close_btn.grab_focus()
    scrolled_window = gtk.ScrolledWindow()
    scrolled_window.set_policy(
        hscrollbar_policy=gtk.POLICY_AUTOMATIC,
        vscrollbar_policy=gtk.POLICY_ALWAYS)
    dialog.vbox.pack_start(scrolled_window)
    listview = make_listview(entries)
    scrolled_window.add_with_viewport(listview)
    return dialog

def make_listview(entries):
    model = gtk.ListStore(str, str, str)
    model.set_sort_column_id(0, gtk.SORT_ASCENDING)
    listview = gtk.TreeView(model)
    cell = gtk.CellRendererText()
    listview.append_column(gtk.TreeViewColumn(_("Name"),  cell, text=0))
    listview.append_column(gtk.TreeViewColumn(_("Mail"),  cell, text=1))
    listview.append_column(gtk.TreeViewColumn(_("Phone"), cell, text=2))
    for entry in entries:
        model.append([entry.name, entry.mail, entry.phone])
    listview.set_model(model)
    return listview

class LDAPEntry(object):
    "This class represents and LDAP entry"
    def __init__(self, attrs):
        self.name = attrs["cn"]
        self.mail = attrs.get("mail") or _("Unknown")
        self.phone = attrs.get("telephoneNumber") or _("Unknown")
        
class LDAPServer(object):
    "This class represents the LDAP server"
    def __init__(self, host, port=389, who="", cred="", base=""):
        self.host = host
        self.port = port
        self.who = who
        self.cred = cred
        self.base = base

    def find_by_name_fragment(self, frag): 
        attributes = ["cn", "mail", "telephoneNumber"]
        pattern = "(|(cn=*%(frag)s*)(sn=*%(frag)s*)(givenName=*%(frag)s*))" \
                    % {"frag" : frag}
        self.ldap = ldap.initialize("ldap://%s:%s" % (self.host, self.port))
        self.ldap.simple_bind_s(self.who, self.cred)
        result = self.ldap.search_s(
            self.base, 
            ldap.SCOPE_SUBTREE, 
            pattern, 
            attributes)
        entries = []
        for _, entry_attrs in result:
            entry = dict.fromkeys(attributes, "")
            for key in entry_attrs:
                entry[key] = entry_attrs[key][0]
            entries.append(LDAPEntry(entry))
        return entries
        
HANDLERS = {
    "LDAPHandler" : {
        "name" : _("LDAP Server"),
        "description" : _("Searches into LDAP servers by name fragment"),
        "requirements" : _check_requirements,
        "categories" : {
            "ldap" : {
                "name" : _("LDAP Entries"),
            }
        },
    }
}
